package so.obligatorio;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;

import so.obligatorio.modelo.FormaPago;
import so.obligatorio.procesos.Cliente;

public class ManejadorColas {
	private static ManejadorColas instance;

	private ManejadorColas() {
		this.semGrandesCompradores = new Semaphore(1);
		this.semDiscapacitados = new Semaphore(1);
		this.semTrjPreferencial = new Semaphore(1);
		this.semOtrosCompradores = new Semaphore(1);

		this.semTerminados = new Semaphore(1);
		this.clientesTerminados = new LinkedList<Cliente>();

		this.grandesCompradores = new LinkedList<Cliente>();
		this.discapacitados = new LinkedList<Cliente>();
		this.tarjetasPreferenciales = new LinkedList<Cliente>();
		this.otrosCompradores = new LinkedList<Cliente>();
	} 

	public static ManejadorColas getInstance() {
		if(instance == null) {
			instance = new ManejadorColas();
		}
		return instance;
	}

	private Semaphore semGrandesCompradores;
	private List<Cliente> grandesCompradores;
	private Semaphore semDiscapacitados;
	private List<Cliente> discapacitados;
	private Semaphore semTrjPreferencial;
	private List<Cliente> tarjetasPreferenciales;
	private Semaphore semOtrosCompradores;
	private List<Cliente> otrosCompradores;
	private List<Cliente> clientes;

	private Semaphore semTerminados;
	private List<Cliente> clientesTerminados;

	public void envejecer() {
		try {

			this.semOtrosCompradores.acquire();
			this.semTrjPreferencial.acquire();
			this.semDiscapacitados.acquire();
			this.semGrandesCompradores.acquire();

			moverDeCola(this.otrosCompradores, this.tarjetasPreferenciales);
			moverDeCola(this.tarjetasPreferenciales, this.discapacitados);

			this.semOtrosCompradores.release();
			this.semTrjPreferencial.release();
			this.semDiscapacitados.release();
			this.semGrandesCompradores.release();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * Pasa los elementos de la cola origen a la cola destino si estos pasaron la cantidad
	 * de turnos maximos que deberia esperar en la cola
	 * @param colaOrigen
	 * @param colaDestino
	 */
	private void moverDeCola(List<Cliente> colaOrigen, List<Cliente> colaDestino) {
		Integer turno = Reloj.getInstance().getTurno();
		Integer turnosEspera = 4;//Hacer parametrizable

		List<Cliente> temp = new ArrayList<Cliente>();
		for (Iterator<Cliente> iteradorOrigen = colaOrigen.iterator(); iteradorOrigen.hasNext();) {
			Cliente c = (Cliente) iteradorOrigen.next();
			if((turno - c.getTurnoEntrada())%turnosEspera == 0) {
				temp.add(c);
				iteradorOrigen.remove();
			}
		}
		for(int i = temp.size()-1; i >= 0; i--) {
			colaDestino.add(0, temp.get(i));
		}
	}

	public void procesarClientes(int cantidadClientes) {

		if (this.clientes.isEmpty()
				&& this.grandesCompradores.isEmpty()
				&& this.discapacitados.isEmpty()
				&& this.tarjetasPreferenciales.isEmpty()
				&& this.otrosCompradores.isEmpty()) {
			Reloj.getInstance().setKeepRunning(false);
			return;
		}

		List<Cliente> tempGrandesCompradores = new ArrayList<Cliente>();
		List<Cliente> tempDiscapacitados = new ArrayList<Cliente>();
		List<Cliente> tempTarjetasPreferenciales = new ArrayList<Cliente>();
		List<Cliente> tempOtrosCompradores = new ArrayList<Cliente>();
		
		Iterator<Cliente> itrClientes = this.clientes.iterator();

		int i = 0;
		while (itrClientes.hasNext() && i<cantidadClientes+1) {
			Cliente c = itrClientes.next();
			itrClientes.remove();
			i++;
			//Marca el turno de entrada del cliente a las colas
			c.setTurnoEntrada(Reloj.getInstance().getTurno());
			if(c.isDiscapacitado()) {
				tempDiscapacitados.add(c);
			} else if (FormaPago.TARJETA_CON_PREFERENCIA.equals(c.getFormaPago())) {
				tempTarjetasPreferenciales.add(c);
			} 
			else if (c.getCantidad()>=40){
				tempGrandesCompradores.add(c);
			}
			else {
				tempOtrosCompradores.add(c);
			}
		}

		try {
			semGrandesCompradores.acquire();
			semDiscapacitados.acquire();
			semTrjPreferencial.acquire();
			semOtrosCompradores.acquire();

			this.grandesCompradores.addAll(tempGrandesCompradores);
			this.discapacitados.addAll(tempDiscapacitados);
			this.tarjetasPreferenciales.addAll(tempTarjetasPreferenciales);
			this.otrosCompradores.addAll(tempOtrosCompradores);

			semGrandesCompradores.release();
			semDiscapacitados.release();
			semTrjPreferencial.release();
			semOtrosCompradores.release();

		} catch (InterruptedException e) {
			e.printStackTrace();
			Reloj.getInstance().setKeepRunning(false);
		}
		
	}

	public Semaphore getSemGrandesCompradores() {
		return semGrandesCompradores;
	}

	public void setSemGrandesCompradores(Semaphore semGrandesCompradores) {
		this.semGrandesCompradores = semGrandesCompradores;
	}

	public List<Cliente> getGrandesCompradores() {
		return grandesCompradores;
	}

	public void setGrandesCompradores(List<Cliente> grandesCompradores) {
		this.grandesCompradores = grandesCompradores;
	}

	public Semaphore getSemDiscapacitados() {
		return semDiscapacitados;
	}

	public void setSemDiscapacitados(Semaphore semDiscapacitados) {
		this.semDiscapacitados = semDiscapacitados;
	}

	public List<Cliente> getDiscapacitados() {
		return discapacitados;
	}

	public void setDiscapacitados(List<Cliente> discapacitados) {
		this.discapacitados = discapacitados;
	}

	public Semaphore getSemTrjPreferencial() {
		return semTrjPreferencial;
	}

	public void setSemTrjPreferencial(Semaphore semTrjPreferencial) {
		this.semTrjPreferencial = semTrjPreferencial;
	}

	public List<Cliente> getTarjetasPreferenciales() {
		return tarjetasPreferenciales;
	}

	public void setTarjetasPreferenciales(List<Cliente> tarjetasPreferenciales) {
		this.tarjetasPreferenciales = tarjetasPreferenciales;
	}

	public Semaphore getSemOtrosCompradores() {
		return semOtrosCompradores;
	}

	public void setSemOtrosCompradores(Semaphore semOtrosCompradores) {
		this.semOtrosCompradores = semOtrosCompradores;
	}

	public List<Cliente> getOtrosCompradores() {
		return otrosCompradores;
	}

	public void setOtrosCompradores(List<Cliente> otrosCompradores) {
		this.otrosCompradores = otrosCompradores;
	}

	public void setColaEntrada(List<Cliente> clientes) {
		this.clientes = clientes;
	}

	public Semaphore getSemTerminados() {
		return semTerminados;
	}

	public List<Cliente> getClientesTerminados() {
		return clientesTerminados;
	}
}
